home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / Futures / FutureShock / EventHandler.cp < prev    next >
Encoding:
Text File  |  1995-07-27  |  12.6 KB  |  499 lines  |  [TEXT/MMCC]

  1. /*================================================================================
  2.     EventHandler.c
  3.     
  4.     ©1991-4 Greg Anderson
  5.     greggor@apple.com
  6.         
  7.     This code handles the main macintosh event loop    
  8. ================================================================================*/
  9. #include <EPPC.h>
  10. #include <AppleEvents.h>
  11. #include <Errors.h>
  12.  
  13. #include "EventHandler.h"
  14. #include "NextEvent.h"
  15. #include "MenuHandler.h"
  16. #include "WindowHandler.h"
  17.  
  18. #include "Main.h"
  19. #include "FutureShockDialog.h"
  20.  
  21. //#include "TEUtilities.h"
  22. #include "MacUtilities.h"
  23. #include "DialogUtilities.h"
  24. #include "ReportError.h"
  25. #include "Exceptions.h"
  26. #include <Threads.h>
  27. //
  28. // Prototypes for private functions:
  29. //
  30. void Idle(EventRecord* nullEvent);
  31.  
  32. void                    ProcessModelessDialog( EventRecord* theEvent  );
  33. Boolean                    ProcessMouseEvent( EventRecord* theEvent  );
  34. void                    SetupCursorShape( Point theMouse, RgnHandle mouseRgn );
  35.  
  36. long gImportantWorkCount = 0;
  37.  
  38. //----------------------------------------------------------------------------------------
  39. // DoingImportantWork
  40. //----------------------------------------------------------------------------------------
  41. Boolean DoingImportantWork()
  42. {
  43.     return gImportantWorkCount > 0;
  44. } // DoingImportantWork
  45.  
  46. //----------------------------------------------------------------------------------------
  47. // SetDoingImportantWork
  48. //
  49. // Call this to reduce the amount of sleep time spent in WaitNextEvent.  Threads
  50. // should do this to keep response time good.
  51. //----------------------------------------------------------------------------------------
  52. void SetDoingImportantWork(Boolean isImportant)
  53. {
  54.     if(isImportant)
  55.         ++gImportantWorkCount;
  56.     else
  57.         --gImportantWorkCount;
  58. } // SetDoingImportantWork
  59.  
  60. //----------------------------------------------------------------------------------------
  61. // Idle: 
  62. //----------------------------------------------------------------------------------------
  63. void Idle(EventRecord* nullEvent)
  64. {
  65.     FrontWindowHandler()->Idle(nullEvent);
  66. } // Idle 
  67.  
  68. //----------------------------------------------------------------------------------------
  69. // HandleEvents: 
  70. //
  71. // Main event processor
  72. //----------------------------------------------------------------------------------------
  73. void HandleEvents( RgnHandle mouseRegion )
  74. {
  75.     EventRecord        theEvent;
  76.     Boolean            isDialogEvent;
  77.     long            theSleep;
  78.     Point            mouse;
  79.     char            keyPressed;
  80.     OSErr            err = noErr;
  81.     
  82.     //
  83.     // This routine watches the cursor as it moves across the
  84.     // window & changes its shape as necessary.
  85.     //
  86.     GetMouse( &mouse );
  87.     LocalToGlobal( &mouse );
  88.     SetupCursorShape( mouse, mouseRegion);
  89.     
  90.     //
  91.     // Be sure to give time to other threads
  92.     //
  93.     YieldToAnyThread();
  94.     
  95.     //
  96.     // Get the next event, whatever type it might be.
  97.     //
  98.     // 'NextEvent' calls either GetNextEvent or WaitNextEvent
  99.     // (preferably the later).  It does some processing
  100.     // to make deactivate, suspend, resume and mouse-moved
  101.     // events easier to interpret.
  102.     //
  103.     // If 'GetNextEvent' is called, mouse-moved events are
  104.     // simulated, so the same cursor-tracking code may be
  105.     // used in multifinder and non-multifinder environments.
  106.     //
  107.     theSleep = DoingImportantWork() ? 10 : GetCaretTime() >> 1;
  108.     isDialogEvent = NextEvent( everyEvent, &theEvent, theSleep, mouseRegion );
  109.  
  110. #ifdef ShowMouseRgn
  111.     SetPort(gWindowMgrPort);
  112.     SetClip(gUniverseRgn);
  113.     InvertRgn(mouseRegion);
  114.     InvertRgn(mouseRegion);
  115. #endif
  116.  
  117.     //
  118.     // It seems that the only way to do accurate mouse tracking
  119.     // is to call SetupCursorShape before and after NextEvent.
  120.     //
  121.     // Passing nil as the mouseRgn prevents all of the complex region
  122.     // calculations from being done twice.
  123.     //
  124.     SetupCursorShape( theEvent.where, nil );
  125.     
  126.     //
  127.     // Set up a failure handler for commands that just cannot be completed
  128.     //
  129.     Try
  130.     {
  131.         Boolean eventHandled = false;
  132.         
  133.         //
  134.         // Here is the dreaded Event Switch Statement.
  135.         //
  136.         // I should write a better event processor -- or better yet,
  137.         // switch to MacApp.  :>
  138.         //
  139.         // A word of warning for the unwary:  NextEvent() mauls
  140.         // theEvent.what, so some of the items in this switch statement
  141.         // might be undefined if GetNextEvent or WaitNextEvent is
  142.         // called directly.  Activate, deactivate, suspend and resume
  143.         // events are all mauled by NextEvent.  See NextEvent.c for
  144.         // code that detects these events (or better yet, include
  145.         // NextEvent.c in your project and use this code as a sample,
  146.         // and you'll be MultiFinder aware).
  147.         //
  148.         switch( theEvent.what )
  149.         {
  150.             //
  151.             // First check for high-level events (e.g. AppleEvents)
  152.             //
  153.             case kHighLevelEvent:
  154.             {
  155.                 AEProcessAppleEvent( &theEvent );
  156.                 eventHandled = true;
  157.                 break;
  158.             }
  159.             
  160.             //
  161.             // If the event is a mousedown event, theEvent.message is undefined,
  162.             // but theEvent.where specifies where the mouse was located when the
  163.             // button went down.
  164.             //
  165.             case mouseDown:    
  166.             {
  167.                 eventHandled = ProcessMouseEvent( &theEvent );
  168.                 break;
  169.             }
  170.             
  171.             //
  172.             // If the event is a keyDown or autoKey event, theEvent.message
  173.             // contains the character code & key code of the key pressed
  174.             // in its low word.
  175.             //
  176.             case autoKey:
  177.             {
  178.                 //
  179.                 // Don't allow autokeys to work with menu equivalents
  180.                 //
  181.                 if( (theEvent.modifiers & cmdKey) != 0 )
  182.                 {
  183.                     eventHandled = true;
  184.                     break;
  185.                 }
  186.             }
  187.             
  188.             case keyDown:
  189.             {
  190.                 keyPressed = (char)(theEvent.message & charCodeMask);
  191.                 
  192.                 //
  193.                 // Handle command-key equivalents of menu functions
  194.                 //
  195.                 if( (theEvent.modifiers & cmdKey) != 0)
  196.                 {
  197.                     SetupMenuItems();
  198.                     ProcessMenuSelection( MenuKey(keyPressed) );
  199.                     eventHandled = true;
  200.                 }
  201.                 else
  202.                 {
  203.                     eventHandled = FrontWindowHandler()->KeyDown(&theEvent, keyPressed);
  204.                 }
  205.                 
  206.                 break;
  207.             }
  208.             
  209.             //
  210.             // If the event is an update event or an activate event,
  211.             // theEvent.message contains a pointer to the window 
  212.             // receiving the event
  213.             //
  214.             case updateEvt:
  215.             {
  216.                 GetWindowHandler((WindowPtr)(theEvent.message))->Update(&theEvent);
  217.                 break;
  218.             }
  219.             
  220.             //
  221.             // An activate event can mean that a window is being activated,
  222.             // OR it could mean that a window is being deactivated.
  223.             //
  224.             // However, NextEvent decodes the event record and returns
  225.             // 'deactivateEvt' if the event was a deactivate; this
  226.             // simplifies this switch statement.  Be careful when copying
  227.             // this code--if you call WaitNextEvent directly, the
  228.             // translation will not be done and this code won't work.
  229.             //
  230.             case activateEvt:
  231.             {
  232.                 GetWindowHandler((WindowPtr)(theEvent.message))->Activate(&theEvent);
  233.                 break;
  234.             }
  235.             
  236.             case deactivateEvt:
  237.             {
  238.                 GetWindowHandler((WindowPtr)(theEvent.message))->Deactivate(&theEvent);
  239.                 break;
  240.             }
  241.             
  242.             //
  243.             // Suspend and resume events are treated like activate and
  244.             // deactivate messages.  (Note that FrontWindow() might
  245.             // return nil; ActivateWindow and DeactivateWindow should
  246.             // catch this and exit gracefully.)
  247.             //
  248.             // Note that NextEvent decodes app4Evt's and returns either
  249.             // resumeEvt, suspendEvt or mouseMovedEvt.
  250.             //
  251.             case resumeEvt:
  252.             {
  253.                 GetWindowHandler((WindowPtr)(theEvent.message))->Resume(&theEvent);
  254.                 break;
  255.             }
  256.             
  257.             case suspendEvt:
  258.             {
  259.                 GetWindowHandler((WindowPtr)(theEvent.message))->Suspend(&theEvent);
  260.                 break;
  261.             }
  262.             
  263.             //
  264.             // The mouseRegion is recalculated on every event,
  265.             // so nothing special needs to be done on mouseMovedEvt.
  266.             //
  267.             case mouseMovedEvt:
  268.             {
  269.                 break;
  270.             }
  271.         }
  272.  
  273.         //
  274.         // Check for modeless dialog events & pass them to the
  275.         // appropriate modeless dialog box.
  276.         //
  277.         // Do not pass events already handled above, though.
  278.         //
  279.         if( isDialogEvent && (eventHandled == false))
  280.         {
  281.             ProcessModelessDialog( &theEvent );
  282.         }
  283.                 
  284.         //
  285.         // Do idle-type-stuff
  286.         //
  287.         Idle(&theEvent);
  288.     }
  289.     Catch(err)
  290.     {
  291.         if((err != eNoWindowHandler) && (err != userCanceledErr))
  292.         {
  293.             //
  294.             // It would be nice if we had better error reporting
  295.             //
  296.             ReportError( "\pThe command could not be completed", err );
  297.         }
  298.     }
  299. } // HandleEvents 
  300.  
  301. //----------------------------------------------------------------------------------------
  302. // ProcessModelessDialog: 
  303. //
  304. // Modeless-dialog event.
  305. //----------------------------------------------------------------------------------------
  306. void ProcessModelessDialog( EventRecord* theEvent  )
  307. {
  308.     DialogPtr            theDialog;
  309.     short                itemHit;
  310.  
  311.     //
  312.     // If the event is a modeless dialog event, pass it to
  313.     // DialogSelect.
  314.     //
  315.     // If DialogSelect returns 'true', that indicates that
  316.     // the user has interacted with the dialog in some way.
  317.     // If this is the case, theDialog will point to the
  318.     // dialog box in question, and itemHit will contain the
  319.     // number of the item clicked on.
  320.     //
  321.     // We determine which dialog was clicked on by examining
  322.     // a field in the record pointed to by the dialog's
  323.     // refCon.
  324.     //
  325.     if( DialogSelect( theEvent, &theDialog, &itemHit ) )
  326.     {
  327.         //
  328.         // Pass the event record and itemHit to the window handler
  329.         //
  330.         GetWindowHandler(theDialog)->DialogManagerEvent(theEvent, itemHit);
  331.     }
  332. } // ProcessModelessDialog 
  333.  
  334. //----------------------------------------------------------------------------------------
  335. // ProcessMouseEvent: 
  336. //
  337. // Mouse-click event.
  338. //
  339. // Strangely enough, theEvent->message doesn't tell us which window
  340. // was clicked on.  The Macintosh toolbox function 'FindWindow' can
  341. // determine this for us, however.
  342. //----------------------------------------------------------------------------------------
  343. Boolean ProcessMouseEvent( EventRecord* theEvent  )
  344. {
  345.     WindowPtr    whichWindow;
  346.     Boolean        eventHandled = false;
  347.     
  348.     switch( FindWindow( theEvent->where, &whichWindow ) )
  349.     {
  350.         case inMenuBar:
  351.         {
  352.             SetupMenuItems();
  353.             ProcessMenuSelection( MenuSelect(theEvent->where) );
  354.             eventHandled = true;
  355.             break;
  356.         }
  357.         
  358.         case inSysWindow:
  359.         {
  360.             SystemClick( theEvent,(WindowPtr)whichWindow );
  361.             eventHandled = true;
  362.             break;
  363.         }
  364.         
  365.         case inContent:
  366.         {
  367.             //
  368.             // If the window clicked on is not frontmost, make it frontmost.
  369.             //
  370.             if( whichWindow != FrontWindow() )
  371.             {
  372.                 SelectWindow(whichWindow);
  373.                 eventHandled = true;
  374.             }
  375.             else
  376.             {
  377.                 eventHandled = GetWindowHandler(whichWindow)->ContentClick(theEvent);
  378.             }
  379.             break;
  380.         }
  381.         
  382.         case inDrag:
  383.         {
  384.             DragWindow( (WindowPtr)whichWindow,theEvent->where,&gUniverseRect);
  385.             eventHandled = true;
  386.             break;
  387.         }
  388.             
  389.         case inGrow:
  390.         {
  391.             GetWindowHandler(whichWindow)->ResizeWindow(theEvent->where);
  392.             eventHandled = true;
  393.             break;
  394.         }
  395.         
  396.         case inGoAway:
  397.         {
  398.             if( TrackGoAway(whichWindow,theEvent->where) )
  399.             {
  400.                 //
  401.                 // We only have one window, so close it
  402.                 //
  403.                 GetWindowHandler(whichWindow)->CloseWindowByUser();
  404.             }
  405.             eventHandled = true;
  406.             break;
  407.         }
  408.     }
  409.     return eventHandled;
  410. } // ProcessMouseEvent 
  411.  
  412. //----------------------------------------------------------------------------------------
  413. // SetupCursorShape: 
  414. //
  415. // Change the shape of the cursor based on its location
  416. //----------------------------------------------------------------------------------------
  417. void SetupCursorShape( Point theMouse, RgnHandle mouseRgn )
  418. {
  419.     OSErr err = noErr;
  420.             
  421.     //
  422.     // Try to set the shape of the cursor
  423.     //
  424.     Try
  425.     {
  426.         WindowPtr inWindow = nil;
  427.         Rect tRect;
  428.         Rect windowRect;
  429.         
  430.         //
  431.         // Is there a window?  If not, fail (error code is ignored, so it
  432.         // doesn't matter what it is)
  433.         //
  434.         inWindow = FrontWindow();
  435.         if(inWindow == nil)
  436.             Throw(-1);
  437.         
  438.         SetPort(inWindow);
  439.         GetGlobalWindowLocation(inWindow, &windowRect);    // inWindow->port.portRect
  440.                 
  441.         //
  442.         // If the point is not inside the front window,
  443.         // then set the cursor to an arrow, and set the mouse
  444.         // region to the universe minus the space taken by
  445.         // the front window
  446.         //
  447.         if(PtInRect(theMouse, &windowRect) == false)
  448.         {
  449.             ChangeCursor(0);
  450.             if(mouseRgn != nil)
  451.             {
  452.                 RectRgn(mouseRgn, &windowRect);
  453.                 XorRgn(mouseRgn, gUniverseRgn, mouseRgn);
  454.             }
  455.         }
  456.         else
  457.         {
  458.             //
  459.             // Do all of our work in local coordinates
  460.             //
  461.             GlobalToLocal(&theMouse);    
  462.  
  463.             //
  464.             // By default, the mouse region is a one-pixel region
  465.             // that just surrounds the cursor.  TWindowHandler::SetupCursorShape
  466.             // may change this region to something else
  467.             //
  468.             SetRect(&tRect, theMouse.h, theMouse.v, theMouse.h + 1, theMouse.v + 1);
  469.             if(mouseRgn != nil)
  470.                 RectRgn(mouseRgn, &tRect);
  471.  
  472.             //
  473.             // If this window doesn't have a window handler, then
  474.             // GetWindowHandler will fail.  If SetupCursorShape
  475.             // returns false, then the handler did not have a specific
  476.             // shape to set the cursor to; set it to an arrow.
  477.             //            
  478.             if(GetWindowHandler(inWindow)->SetupCursorShape(theMouse, mouseRgn) == false)
  479.                 ChangeCursor(0);
  480.             
  481.             //
  482.             // Translate the region back to global coordinates
  483.             //
  484.             if(mouseRgn != nil)
  485.                 RgnToGlobal(mouseRgn);
  486.         }
  487.     }
  488.     Catch(err)
  489.     {
  490.         //
  491.         // If we couldn't handle the SetupCursorShape event, then
  492.         // the cursor is an arrow everywhere
  493.         //
  494.         ChangeCursor(0);
  495.         if(mouseRgn != nil)
  496.             RectRgn(mouseRgn, &gUniverseRect);
  497.     }
  498. } // SetupCursorShape 
  499.